home *** CD-ROM | disk | FTP | other *** search
/ Precision Software Appli…tions Silver Collection 1 / Precision Software Applications Silver Collection Volume One (PSM) (1993).iso / tutor / asm1tut.exe / CHAP17.DOC < prev    next >
Text File  |  1990-07-20  |  23KB  |  502 lines

  1.  
  2.  
  3.  
  4.                                                                            177
  5.  
  6.                                 CHAPTER 17 - INTERRUPTS
  7.  
  8.  
  9.              Your word processor will work on a Compaq, an IBM, an AST or any
  10.              other type of PC compatible computer. Every time it wants to read
  11.              a file from disk or write to a printer, it calls a DOS or BIOS
  12.              function that takes care of it.{1}  Yet every computer has its
  13.              own versions of these subroutines, and they not only are
  14.              different, they are in different places in memory. How does the
  15.              word processor know where to find them? It uses interrupts.{2}
  16.  
  17.              An interrupt is a glorified subprogram call. The first 1024 bytes
  18.              of your computer's memory (that's from 0000:0000 to 0000:03FF)
  19.              contain the addresses of all the DOS and BIOS interrupts. Which
  20.              address contains the address of which subprogram was decided by
  21.              the triumverate of Intel/IBM/Microsoft. We have two different
  22.              sets of addresses here, so let's keep them straight. Starting at
  23.              memory address 0000 there are 4 byte addresses called interrupt
  24.              vectors. There is a different vector at 0000d, at 0004d, at
  25.              0008d, at 0012d at 0016d etc.; there are 256 of them in all. Each
  26.              of these 256 places can hold the address of a subprogram
  27.              somewhere in memory (although not all of them are used). 
  28.  
  29.              When you call an interrupt, the 8086 goes to the appropriate
  30.              place in low memory (from 0000 to 3FFh) and finds the address of
  31.              the subprogram that you want, loads it into CS and IP, and goes
  32.              to that subprogram. When that subprogram is done, it goes back to
  33.              the next instruction in your program after the interrupt. 
  34.  
  35.              How did those addresses get into the first 1024 bytes? The
  36.              computer put them there when you started it up. It is one of the
  37.              first things that the computer did when you turned it on. These
  38.              subprograms do EVERYTHING, and you can't run the computer without
  39.              them. 
  40.  
  41.              If you want to scroll the screen, you put the appropriate
  42.              information in the 8086 registers and use:
  43.  
  44.                  int 10h             ; decimal 16 {3}
  45.  
  46.              The 8086 goes to the interrupt 16 address (4*16 = 64), gets the
  47.              address of the program that contains the video subprograms, and
  48.              ____________________
  49.  
  50.                 1. BIOS stands for basic input/output services.
  51.  
  52.                 2. If you haven't gotten it yet, it's time to get either "DOS
  53.              Programmer's Reference" or "The Peter Norton Programmer's Guide
  54.              to the IBM PC." I'm serious. They contain the information you
  55.              need to make use of this chapter.
  56.  
  57.                 3. It is normal to use hex numbers for the interrupts, so if
  58.              you read about an interrupt make sure you know if the numbers are
  59.              hex or decimal.
  60.  
  61.              ______________________
  62.  
  63.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  64.  
  65.  
  66.  
  67.  
  68.              The PC Assembler Tutor                                        178
  69.              ______________________
  70.  
  71.              goes to it. If you want to write to the printer, you put the
  72.              appropriate information in the 8086 registers and call:
  73.  
  74.                  int 21h             ; decimal 33
  75.  
  76.              The 8086 goes to address 132 (4*33 = 132), gets the address of
  77.              the DOS program, puts it in CS and IP, and starts it. If you want
  78.              to get input from the keyboard, you put the appropriate
  79.              information in the 8086 registers and call:
  80.  
  81.                  int  21h            ; decimal 33
  82.  
  83.              That's right, it is the same program as the one that does the
  84.              printer. The 8086 goes to 132 (4*33), gets the program address,
  85.              and goes to it. 
  86.  
  87.              The lowest interrupt is int 0 (address = 4*0 = 0000). The highest
  88.              interrupt is int 255 (address = 4*255 = 1020). 
  89.  
  90.              This is an intelligent way to handle the situation. As long as
  91.              everyone agrees which interrupt contains the address of which
  92.              subprogram, our programs will work on any PC compatible. This is
  93.              one of the things that is meant by PC compatible. 
  94.  
  95.              On my computer, here is a section of these addresses starting
  96.              with int 1Eh (30d). High memory is at the top, low memory is at
  97.              the bottom.
  98.  
  99.                  INT #           DATA       LOCATION
  100.  
  101.                                 0724h cs       146
  102.                    36           04A8h ip       144
  103.                              cs 0724h          142
  104.                    35        ip 01BDh          140
  105.                                 0724h cs       138
  106.                    34           01B0h ip       136
  107.                              cs 019Fh          134
  108.                    33        ip 05EBh          132
  109.                                 019Fh cs       130
  110.                    32           05E7h ip       128
  111.                              cs 0000h          126
  112.                    31        ip 0000h          124
  113.                                 0070h cs       122
  114.                    30           0EB8h ip       120
  115.  
  116.              If we call int 30d, the 8086 goes to 120 (4*30), puts 0EB8h into
  117.              the IP, and puts 0070h (from the next higher location) into CS.
  118.              The next instruction it does will be 0070:0EB8. If we call int
  119.              35d, the 8086 goes to 140 (4*35), puts 01BDh in IP, puts 0724h
  120.              (from the next higher location) in CS. The next instruction the
  121.              8086 does will be 0724:01BD. If we call int 33d, the 8086 goes to
  122.              132 (4*33), and puts 05EBh in IP, 019Fh  (from the next higher
  123.              location) in CS. The next instruction executed will be
  124.              019F:05EBh. The 0000:0000 for int 31d indicates that there is no
  125.              int 31d (address 0000:0000 contains data, [the vectors for int
  126.              0], not a program). Make sure that you understand how this is
  127.              working before you go on.
  128.  
  129.  
  130.  
  131.  
  132.              Chapter 17 - Interrupts                                       179
  133.              _______________________
  134.  
  135.  
  136.              On the PC, information for the interrupts is always passed
  137.              through registers, not on the stack. Each interrupt type has a
  138.              specific register for each piece of information it needs. We will
  139.              do a couple to see how they work.
  140.  
  141.  
  142.              DISPLAYING A CHARACTER
  143.  
  144.              We can print a character at a time on the monitor, so we'll input
  145.              a string, and then print out each character of the string
  146.              individually. We will stop the printout when we see the 0 at the
  147.              end of the string.
  148.  
  149.              ; - - - - - - - - - - START DATA BELOW THIS LINE
  150.              buffer  db  80 dup (?)
  151.              ; - - - - - - - - - - START DATA BELOW THIS LINE
  152.              ; - - - - - - - - - - START CODE BELOW THIS LINE
  153.                  call show_regs
  154.              outer_loop:
  155.                  mov  ax, offset buffer
  156.                  call get_string
  157.  
  158.                  mov  si, offset buffer
  159.              inner_loop:
  160.                  mov  al, [si]
  161.                  cmp  al, 0
  162.                  je   next_string
  163.                  mov  ah, 14              ; ah contains function number
  164.                  mov  bh, 0               ; where in memory
  165.                  int  10h                 ; 33d
  166.                  inc  si
  167.                  jmp  inner_loop
  168.  
  169.              next_string:
  170.                  call get_continue
  171.                  jmp  outer_loop
  172.  
  173.              ; - - - - - - - - - - START CODE BELOW THIS LINE
  174.  
  175.              The program is simple. It gets a string, then checks each
  176.              character for 0 (end of string), before shipping it off to the
  177.              screen. I'll explain AH in a second. There are several places
  178.              this character can be displayed{4}, so use show_regs to force the
  179.              video screen to a certain place in memory, then put BH = 0 to
  180.              tell the interrupt that the screen memory is in that place.
  181.              Finally, with all the information in place, we do the interrupt.
  182.              You will not print a carriage return, only the data, so we use
  183.              get_continue to give us a carriage return. It's a little sloppy,
  184.              but much easier. 
  185.  
  186.              We have lots and lots of subprograms for the disks, printer,
  187.              screen, etc. There are only 255 interrupts, so it was decided at
  188.              the beginning to make most of the interrupts groups of programs
  189.              ____________________
  190.  
  191.                 4. Cf. one of those two books.
  192.  
  193.  
  194.  
  195.  
  196.              The PC Assembler Tutor                                        180
  197.              ______________________
  198.  
  199.              instead of a single program. Int 10h (16d) contains about two
  200.              dozed different video subprograms. Each program is distinguished
  201.              by a specific number in AH. For int 10h (16d):
  202.  
  203.                  ah = 2h        Get cursor position
  204.                  ah = 6h        Scroll window up
  205.                  ah = Eh        (14d) Write character to screen
  206.  
  207.              For int 21h (33d):
  208.  
  209.                  ah = 1h        Keyboard input
  210.                  ah = 5h        Printer output
  211.                  ah = 17h       Rename a file
  212.                  ah = 2Ch       Get the time
  213.  
  214.              As you can see, int 21h is a potpourri of subprograms. We'll do
  215.              the same program as above, but with printer output. Everything is
  216.              the same except that the inner loop should be changed to look
  217.              like this:
  218.  
  219.              ; - - - - - 
  220.                  mov  si, offset buffer
  221.              inner_loop:
  222.                  mov  dl, [si]
  223.                  cmp  dl, 0
  224.                  je   next_string
  225.                  mov  ah, 5               ; ah contains function number
  226.                  int  21h                 ; 33d
  227.                  inc  si
  228.                  jmp  inner_loop
  229.  
  230.              ; - - - - -
  231.  
  232.              There is practically no change. We use DL instead of AL, have
  233.              "int 21h" instead of "int 10h", and change the function number in
  234.              AH to 5. Also, BH is not needed.
  235.  
  236.              Leave your printer off at the beginning to see what happens. Turn
  237.              your printer on and enter a string of 10 or 20 letters. Probably
  238.              nothing happened. Enter another 20 or 30 letters. Nothing again.
  239.              Try 50 letters this time. This time it should work. Lots of
  240.              printers won't print anything unless (1) they get a carriage
  241.              return (which you haven't sent) or (2) they have a backlog of
  242.              more than 80 characters. If you ever use the printer interrupt,
  243.              you'll have to remember that.
  244.  
  245.              These interrupts which are in your program are called software
  246.              interrupts. They are your interface with the peripheral devices
  247.              on your machine, doing everything from disk i/o to handling
  248.              memory allocation. They do all your housekeeping for you.{5}  If
  249.              you are going to work at this level then you should buy one of
  250.              those two books. They contain all the software interrupts (and
  251.              there are about a hundred of them), tell how they work and which
  252.              registers to set for each interrupt. If you don't have access to
  253.              ____________________
  254.  
  255.                 5. But they don't do Windows.
  256.  
  257.  
  258.  
  259.  
  260.              Chapter 17 - Interrupts                                       181
  261.              _______________________
  262.  
  263.              these interrupts it's like having your arms cut off - you're
  264.              unable to do any i/o at all.
  265.  
  266.  
  267.              HARDWARE INTERRUPTS
  268.  
  269.              The interrupts that we write in our programs aren't the only
  270.              interrupts there are. The hardware uses interrupts to take
  271.              temporary control of the computer. On a Macintosh, if you insert
  272.              a disk, the program stops and the operating system reads in the
  273.              disk directory. A modem that is in use will request time for
  274.              doing i/o. Also, if the 8086 detects a zero divide, it will
  275.              trigger a special interrupt. You have met int 4 already. It is
  276.              the INTO instruction, which will trigger an interrupt if the
  277.              overflow flag is set. 
  278.  
  279.              Your interrupts are in specific places in your code, but these
  280.              machine interrupts can happen at any time. There are two lines
  281.              (wires) into the 8086. One is for serious problems that need to
  282.              be taken care of NOW, and it has non-maskable interrupts. The
  283.              other line is for interrupts that need to be taken care of in a
  284.              timely fashion, and they are maskable interrupts. 
  285.  
  286.              A non-maskable interrupt (NMI) is when the hardware detects that
  287.              it is in deep doo doo. It sends a signal on the NMI line that
  288.              says "Hey! I need an interrupt." The 8086 finishes the
  289.              instruction it is processing and then IMMEDIATELY gives over
  290.              control. The NMI uses the same 1024 bytes in low memory for
  291.              interrupt vectors, but has its own interrupt numbers. Normally
  292.              this is for very serious errors, so the interrupt program may
  293.              decide to abort your program and return to the operating system;
  294.              if it makes sense to, it will return to your program where your
  295.              program left off.
  296.  
  297.              A maskable interrupt is when a piece of hardware has some work to
  298.              do. It sends a signal to the 8086 (on the INTR line), and the
  299.              8086 takes care of it when it is ready.
  300.  
  301.              When the 8086 is ready depends on you. In the flags register is
  302.              the IEF, the interrupt enable flag. It should always be set to 1
  303.              unless you are doing something critical. Basically, the only
  304.              things that are critical are interrupts themselves and context
  305.              switches. Context switches are done by the operating system in
  306.              multitasking environments, so they don't concern you, and you are
  307.              not writing interrupts, so they don't concern you. Therefore,
  308.              always keep the IEF set. Just for your information, you set the
  309.              IEF with:
  310.  
  311.                  sti  ; set interrupt flag (interrupts enabled)
  312.  
  313.              and clear it with:
  314.  
  315.                  cli  ; clear interrupt flag (interrupts disabled)
  316.  
  317.              Why do interrupt programs clear the interrupt flag? Because you
  318.              could have interrupts interrupting other interrupts and wind up
  319.              with scads of half finished interrupts lying around. This way,
  320.  
  321.  
  322.  
  323.  
  324.              The PC Assembler Tutor                                        182
  325.              ______________________
  326.  
  327.              one interrupt finishes before another can take over. 
  328.  
  329.  
  330.              Suppose you are in the middle of the following two instructions
  331.              when an interrupt hits:
  332.  
  333.                  cmp  al, 7
  334.                  jne  some_label
  335.  
  336.              If the hardware interrupt takes over after the CMP instruction
  337.              but before the JNE instruction, the correctness of the result
  338.              will depend on the zero flag staying the same. Can you trust it?
  339.              The answer is yes. The first three things an interrupt request
  340.              does (in the microcode) are:
  341.  
  342.                  push flags     ; push the flags
  343.                  push old CS    ; push the code segment
  344.                  push old IP    ; push the instruction pointer
  345.  
  346.              On return, it pops the flags back in place, so they are exactly
  347.              the same as just before the interrupt. You will notice that there
  348.              are 3 things on the stack instead of the usual 2 in a far call.
  349.              Therefore, there is a special RET instruction called IRET (return
  350.              from interrupt) which pops not only IP and CS, but the flags as
  351.              well. You can only use this instruction in an interrupt because
  352.              it assumes that the flags register is right after CS on the
  353.              stack.
  354.  
  355.  
  356.              DEBUGGING
  357.  
  358.              Finally, if you have a debugger installed, the debugger uses two
  359.              interrupts, int 1 and int 3. You code int 3 into the program by
  360.              placing:
  361.  
  362.                  int
  363.  
  364.              in the program with no number.{6}  The interrupt will call the
  365.              debugger. The reason for this int with no number is that
  366.              afterwards, you can replace it with NOP, since they are both 1
  367.              byte instructions. Int 3 is a 2 byte instruction (they are coded
  368.              differently, even though they wind up at the same place).
  369.  
  370.              Int 1 is the trap instruction. In the flags register is the trap
  371.              flag (TF). If TF is set, the 8086 will interrupt after the next
  372.              instruction. That way, the debugger can single step through
  373.              sections of code. You put Int 3 to set a breakpoint and then set
  374.              TF to single step from there. 
  375.  
  376.              The only way to set TF from your own program is:
  377.  
  378.              ____________________
  379.  
  380.                 6. Though the Microsoft assembler considers this an error. You
  381.              must code it as:
  382.  
  383.                  int  3
  384.  
  385.  
  386.  
  387.  
  388.              Chapter 17 - Interrupts                                       183
  389.              _______________________
  390.  
  391.                  pushf
  392.                  pop  ax
  393.                  or   ax, 0100h
  394.                  push ax
  395.                  popf
  396.  
  397.              but you should let the debugger do it.
  398.  
  399.              Just so you can get a feel for how the debugger system works,
  400.              there is a pseudo-debugger called PSEUDDBG.COM in \XTRAFILE. It
  401.              is not a debugger. It does nothing but tell you whether you have
  402.              generated a breakpoint interrupt (int 3) or a single_step
  403.              interrupt (int 1). It is memory resident. That means that once
  404.              you have loaded it, it will stay in memory until you shut the
  405.              machine off or reset the machine. You load it by executing:
  406.  
  407.                  >pseuddbg
  408.  
  409.              It will tell you that it has been loaded and then sit there
  410.              waiting for interrupts. When you generate a breakpoint interrupt,
  411.              PSEUDDBG will allow you to set the trap flag or just continue.
  412.              When you generate a single step interrupt, PSEUDDBG will allow
  413.              you to clear TF or to continue single stepping. All you need to
  414.              try it out is a simple program:
  415.  
  416.              ; - - - - - ENTER CODE BELOW THIS LINE
  417.              outer_loop:
  418.                  call get_continue
  419.  
  420.                  mov  cx, 4
  421.                  int  3
  422.              inner_loop:
  423.                  mov  ax, 5
  424.                  add  ax, 3
  425.                  add  ax, 7
  426.                  add  ax, 10
  427.                  loop inner_loop
  428.  
  429.                  loop outer_loop
  430.              ; - - - - - FINISH CODE ABOVE THIS LINE
  431.  
  432.              Each time you start the outer loop, it will generate a breakpoint
  433.              interrupt. At this point you can set TF to single step or
  434.              continue. If you single step, watch IP. It will be changing,
  435.              cycling through the loop till it gets to get_continue. If you
  436.              start trapping through get_continue, things will get strange
  437.              because get_continue stores the flags. This means that after you
  438.              quit trapping, get_continue will POP some flags that have TF set
  439.              and it will start trapping again. If this happens, just continue
  440.              hitting 0 until you get out of the single step mode. 
  441.  
  442.              If you like this method of single stepping through code, there is
  443.              a memory resident version of ASMHELP called HELPMEM.COM which
  444.              contains show_regs and allows single stepping. It can be used in
  445.              conjunction with ASMHELP.OBJ. For details consult APP1.DOC in the
  446.              appendix.
  447.  
  448.  
  449.  
  450.  
  451.  
  452.              The PC Assembler Tutor                                        184
  453.              ______________________
  454.  
  455.                                           SUMMARY
  456.  
  457.              Software interrupts send the 8086 to the first 1024 bytes of
  458.              memory where it finds the address of the DOS or BIOS subprogram
  459.              that you want to go to. The correspondence between the interrupt
  460.              number and the location of the address is:
  461.  
  462.                  address location = 4 * interrupt number
  463.  
  464.              The address is stored in the next 4 bytes; first IP, then CS.
  465.  
  466.  
  467.  
  468.              The first 5 interrupts are:
  469.  
  470.                  int 0     divide by zero
  471.                  int 1     trap flag induced single stepping for the debugger
  472.                  int 2     non_maskable_interrupt (NMI)
  473.                  int 3     1 byte interrupt for the debugger
  474.                  int 4     interrupt on overflow
  475.  
  476.  
  477.              There are two types of hardware interrupts. NMI (non maskable
  478.              interrupt) interrupts are for serious problems that need to be
  479.              taken care of IMMEDIATELY. They have priority and take over right
  480.              after the current instruction is finished.
  481.  
  482.              Maskable interrupts are hardware interrupts that should be taken
  483.              care of in a timely fashion. If IEF (the interrupt enable flag)
  484.              is set the maskable interrupts will go through. If IEF is
  485.              cleared, the maskable interrupts must wait until it is set again.
  486.              Set the IEF with:
  487.  
  488.                  sti  ; set interrupt flag
  489.  
  490.              and clear it with:
  491.  
  492.                  cli  ; clear interrupt flag
  493.  
  494.              The IEF should always stay set unless there is a specific reason
  495.              for not allowing interrupts to happen.
  496.  
  497.  
  498.              If you are writing an interrupt routine, then instead of RET you
  499.              use IRET (return from interrupt) which not only pops off IP and
  500.              CS, but pops the flags register as well.
  501.  
  502.